#ifndef EVALUATOR_HPP
#define EVALUATOR_HPP

#include <vector>
#include <array>
#include <cstdint>
#include <stdexcept>
#include "prng.h"
#include "encryptor.h"
#include "utils.h"
#include "context.h"
#include "ring_scaling.h"
#include "ring.h"
#include "ring_poly.h"
#include "operand.h"
#include "ring_sampler.h"
#include "keygen.h"
#include "ring_basis_extension.h"
#include "modular_reduction.h"
#include "encryptor.h"
#include "decryptor.h"
#include "key_switcher.h"
/* *
 * @class Evaluator
 * @brief 用于同态加密操作的评估器类，支持加密数据的各种操作。
 *
 * `Evaluator` 类提供了同态加密中多种运算的实现，包括加法、乘法、旋转、重线性化等操作。
 * 它主要用于对密文进行处理和评估，以支持同态加密中的加法、乘法、转换等操作。
 */
class Evaluator {
public:
    Context *params;
    Ring *ringQ;
    Ring *ringP;
    Ring *ringQMul;
    uint64_t t;
    mpz_class *pHalf;
    FastBasisExtender *baseconverterQ1Q2;
    FastBasisExtender *baseconverterQ2Q1;
    std::vector<std::vector<Poly *>> poolQ;
    std::vector<std::vector<Poly *>> poolQMul;
    Plaintext *tmpPt;
    Encoder *lightEncoder;
    RelinearizationKey *rlk;
    RotationKeySet *rtks;
    KeySwitcher *keySwitcher;

    Evaluator(Context *params, EvaluationKey *evaluationKey);
    explicit Evaluator(Context *params);
    // 拷贝构造函数
    Evaluator(const Evaluator &) = delete;
    Evaluator &operator = (const Evaluator &) = delete;
    // 移动构造函数
    Evaluator(Evaluator &&other) = delete;
    Evaluator &operator = (Evaluator &&other) = delete;
    ~Evaluator();

    void Mul(Ciphertext *op0, Operand *op1, Ciphertext *ctOut, MultiType type);
    void TensorAndRescale(Ciphertext *ct0, Ciphertext *ct1, Ciphertext *ctOut);
    void ModUpAndNTT(Ciphertext *ct, std::vector<Poly *> &cQ, std::vector<Poly *> &cQMul);
    void TensoreLowDeg(Ciphertext *ct0, Ciphertext *ct1);
    void TensortLargeDeg(Ciphertext *ct0, Ciphertext *ct1);
    void Quantize(Ciphertext *ctOut);
    void MulPlaintextMul(Ciphertext *ct0, PlaintextMul *ptRt, Ciphertext *ctOut) const;
    void MulPlaintextRingT(Ciphertext *ct0, PlaintextRingT *ptRt, Ciphertext *ctOut);
    std::tuple<Ciphertext *, Ciphertext *, Ciphertext *> GetElemAndCheckBinary(Operand *op0, Operand *op1,
        Operand *opOut, uint64_t opOutMinDegree, bool ensureRingQ);
    void WithKey(EvaluationKey *evaluationKey);
    void RotateRows(Ciphertext *ct0, Ciphertext *ctOut);
    Ciphertext *RotateRowsNew(Ciphertext *ct0);
    void Permute(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey, Ciphertext *ctOut) const;
    void ApplyGalois(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey, Ciphertext *ctOut) const;
    void ApplyGaloisInplace(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey) const;
    void RotateColumns(Ciphertext *ct0, int k, Ciphertext *ctOut) const;
    Ciphertext *RotateColumnsNew(Ciphertext *ct0, int k);
    void InnerSum(Ciphertext *ct0, Ciphertext *ctOut);
    void SwitchKeys(Ciphertext *ct0, SwitchingKey *switchKey, Ciphertext *ctOut);
    Ciphertext *SwitchKeysNew(Ciphertext *ct0, SwitchingKey *switchkey);
    void Relinearize(Ciphertext *ct0, Ciphertext *ctOut);
    Ciphertext *RelinearizeNew(Ciphertext *ct0);
    void Neg(Operand *op, Ciphertext *ctOut);
    void Sub(Operand *op0, Operand *op1, Ciphertext *ctOut);
    void SubNoMod(Operand *op0, Operand *op1, Ciphertext *ctOut);
    void Reduce(Operand *op, Ciphertext *ctOut);
    Ciphertext *GetRingQElem(Operand *op);
    std::tuple<Ciphertext *, Ciphertext *, bool> GetSmallestLargest(Ciphertext *el0, Ciphertext *el1);
    void MulScalar(Operand *op, uint64_t scalar, Ciphertext *ctOut) const;
    void AddScalar(Operand *op, uint64_t scalar, Ciphertext *ctOut) const;
    void Add(Operand *op0, Operand *op1, Ciphertext *ctOut);
    void EvaluateInPlaceBinary(Ciphertext *el0, Ciphertext *el1, Ciphertext *elOut,
        std::function<void(Poly *, Poly *, Poly *)> evaluate);
    Ciphertext *AddNew(Ciphertext *op0, Ciphertext *op1);
    void AddNoMod(Operand *op0, Operand *op1, Ciphertext *ctOut);
    Ciphertext *AddNoModNew(Ciphertext *op0, Ciphertext *op1);

private:
    void PrivateRelinearize(Ciphertext *ct0, Ciphertext *ctOut);
};

#endif // EVALUATOR_HPP